// @(#)NodePositionTracker.java   7/2003
// Copyright (c) 1998-2003, Distributed Real-time Computing Lab (DRCL) 
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// 
// 1. Redistributions of source code must retain the above copyright notice,
//    this list of conditions and the following disclaimer. 
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution. 
// 3. Neither the name of "DRCL" nor the names of its contributors may be used
//    to endorse or promote products derived from this software without specific
//    prior written permission. 
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// 

package drcl.inet.mac;

import drcl.comp.*;
import drcl.net.*;
import drcl.inet.*; 
import drcl.inet.contract.*;
import java.util.*;
import drcl.comp.Port;
import drcl.comp.Contract;

/**
* This component divides the X-Y plane of the simulated space area into multiple
* subareas. Each mobile node's <code>MobiltiyModel</code> periodically reports
* its position to <code>NodePositionTracker</code>. 
* When a <code>NodePositionTracker</code> receives a neighboring node inquery from
* the <node>Channel</code> component, the <code>NodePositionTracker</code> 
* first finds out which subarea that sender node is located and 
* returns the nid's of all mobile nodes which are currently located in all neighbouring
* subareas.  
* 
* @author Ye Ge
* @see Channel
* @see MobilityModel 
*
*/
public class NodePositionTracker extends drcl.net.Module {

	/* configurate the ports */
	private static final String NODE_PORT_ID    = ".node";          // connect to the mobile nodes' wirelessphy   
	private static final String CONFIG_PORT_ID  = ".config";
	private static final String CHANNEL_PORT_ID = ".channel";    // connect to the channel component
	
	private Port nodePort    = addPort(NODE_PORT_ID, false);
	private Port configPort  = addPort(CONFIG_PORT_ID, false); 
	private Port channelPort = addServerPort(CHANNEL_PORT_ID);

	{
		removeDefaultUpPort();
		removeDefaultDownPort();
		removeTimerPort();
	}    
	
	/** The grid size along X-axle */
	public double dX;
	
	/** The grid size along Y-axle */
	public double dY;
	
	/** The largest x coordinate value of the simulated area */
	public double maxX;
	/** The largest y coordinate value of the simulated area */
	public double maxY;
	/** The smallest x coordinate value of the simulated area */
	public double minX;
	/** The smallest y coordinate value of the simulated area */
	public double minY;    
	
	/** Number of subareas along the X-axle. */
	public int    m;
	/** Number of subareas along the X-axle. */
	public int    n;
	
	/** 
	* Each element of this two dimensional array is 
	* a vector to hold the ids of all mobile nodes 
	* currently are located in that subarea.
	*/
	public Vector g[][];
	
	/**
	* Construction function.
	*
	* @param maxX_ the largest x coordinate value of the simulated area
	* @param minX_ the smallest x coordinate value of the simulated area
	* @param maxY_ the largest y coordinate value of the simulated area
	* @param minY_ the smallest y coordinate value of the simulated area
	* @param dX_   the grid size along X-axle
	* @param dY_   the grid size along Y-axle
	*/
	public NodePositionTracker(double maxX_, double minX_, double maxY_, double minY_, double dX_, double dY_) {
		super();
		maxX = maxX_; minX = minX_; dX = dX_;
		maxY = maxY_; minY = minY_; dY = dY_;
		m = ((int)((maxX - minX)/dX)) + 1;
		n = ((int)((maxY - minY)/dY)) + 1;
		g = new Vector[m][n];
		//System.out.println("size of g "+m+" "+n);
		for (int i = 0; i < m; i ++ ) 
			for (int j = 0; j < n; j ++ ) 
				g[i][j] = new Vector();
	}

	public NodePositionTracker( ) {
		super();
	}
	
	public String info()
	{
		StringBuffer sb0= new StringBuffer();
		sb0.append("Xrange/dX = " + minX + "--" + maxX + "/" + dX + "\n");
		sb0.append("Yrange/dY = " + minY + "--" + maxY + "/" + dY + "\n");
		StringBuffer sb= new StringBuffer();
		for (int i=0; i<m; i++)
			for (int j=0; j<n; j++)
				if (!g[i][j].isEmpty()) {
					sb.append("   (" + i + "," + j + "):");
					for (Iterator it=g[i][j].iterator(); it.hasNext(); ) {
						sb.append(it.next() + " ");
					}
					sb.append("\n");
				}
		if (sb.length() == 0)
			return sb0 + "No node has reported position.\n";
		else
			return sb0 + "Node Positions:\n" + sb;
	}
	
	/**
	* Sets the grid parameters to divide the simulated area into subareas.
	*
	* @param maxX_ the largest x coordinate value of the simulated area
	* @param minX_ the smallest x coordinate value of the simulated area
	* @param maxY_ the largest y coordinate value of the simulated area
	* @param minY_ the smallest y coordinate value of the simulated area
	* @param dX_   the grid size along X-axle
	* @param dY_   the grid size along Y-axle
	*/
	public void setGrid(double maxX_, double minX_, double maxY_, double minY_, double dX_, double dY_) {
		//System.out.println("setGrid maxX_ = " + maxX_ + " minX_ = " + minX_ + " maxY_ = " + maxY_ + " minY_ = " + minY_ + "dX = " + dX_ + " dY_ = " + dY_ );        
		maxX = maxX_; minX = minX_; dX = dX_;
		maxY = maxY_; minY = minY_; dY = dY_;
		m = ((int)((maxX - minX)/dX)) + 1;
		n = ((int)((maxY - minY)/dY)) + 1;
		g = new Vector[m][n];
		for (int i = 0; i < m; i ++ ) 
			for (int j = 0; j < n; j ++ ) 
				g[i][j] = new Vector();
	}    

	public String getName() { return "NodePositionTracker"; }
	
	public void duplicate(Object source_)
	{ 
		super.duplicate(source_);
		NodePositionTracker that_ = (NodePositionTracker)source_;
		maxX = that_.maxX; minX = that_.minX; dX = that_.dX;
		maxY = that_.maxY; minY = that_.minY; dY = that_.dY;

		m = ((int)((maxX - minX)/dX)) + 1;
		n = ((int)((maxY - minY)/dY)) + 1;
		g = new Vector[m][n];
		for (int i = 0; i < m; i ++ ) 
			for (int j = 0; j < n; j ++ ) 
				g[i][j] = new Vector();
	}    

	protected void processOther(Object data_, Port inPort_)
	{
		String portid_ = inPort_.getID();
	
		if (portid_.equals(NODE_PORT_ID)) {
		/*
			if (!(data_ instanceof PositionReportContract.Message)) {
				error(data_, "processOther()", inPort_, "unknown object");
				return;
			}
			*/
			processReport(data_, inPort_);
		}    
		else if (portid_.equals(CHANNEL_PORT_ID)) {
		/*
			if (!(data_ instanceof NeighborQueryContract.Message)) {
				error(data_, "processOther()", inPort_, "unknown object");
				return;
			}
			*/
			processQuery(data_, inPort_);
		}
		else 
			super.processOther(data_, inPort_);
	}  

	/**
	* Processes the position update reports sent from the <code>MobilityModel</code>
	* of the mobile nodes
	*/
	protected void processReport(Object data_, Port inPort_) {
		//debug("received position report" + data_.toString());        
		
		if ( !(data_ instanceof PositionReportContract.Message) ) {
			error(data_, "processReport()", inPort_, "unknown object");
			return;
		}    
		PositionReportContract.Message msg = (PositionReportContract.Message) data_;
		
		long id;  
		double X, Y, Z, X0, Y0, Z0;

		id = msg.getNid();
		X  = msg.getX();
		Y  = msg.getY();
		Z  = msg.getZ();
		X0  = msg.getX0();
		Y0  = msg.getY0();
		Z0  = msg.getZ0();
		
		int i, j, i0, j0;
		
		if ( X == X0 && Y == Y0 )  {  // first time or not moved yet 
			//System.out.println("minX "+minX+ " X "+X+" dX "+dX);
			//System.out.println("minY "+minY+ " Y "+Y+" dY "+dY);
			i = (int)((X-minX)/dX);
			j = (int)((Y-minY)/dY);
			//System.out.println("i "+i+" j "+j);
			if ( g[i][j].contains(new Long(id)) == false )
				g[i][j].insertElementAt(new Long(id), 0);
		}
		else {
			i = (int)((X-minX)/dX);
			j = (int)((Y-minY)/dY);
			i0 = (int)((X0-minX)/dX);
			j0 = (int)((Y0-minY)/dY);

			g[i0][j0].remove(new Long(id));
			g[i][j].insertElementAt(new Long(id), 0);
		}    
	}
	
	/**
	* Processes the neighbouring node inquery.
	*/
	protected void processQuery(Object data_, Port inPort_) {
		if ( !(data_ instanceof NeighborQueryContract.Message) )  {
			error(data_, "processQuery()", inPort_, "unknown object");
			return;
		}
		
		NeighborQueryContract.Message msg = (NeighborQueryContract.Message) data_;
		
		long id;  
		double X, Y, Z;
		long[] nodeList;
		int  nGrids;

		X  = msg.getX();
		Y  = msg.getY();
		Z  = msg.getZ();
		nGrids = msg.getnGrids();
		
		int i, j, il, ir, jl, jr;
		
		i = (int)((X-minX)/dX);
		j = (int)((Y-minY)/dY);
		il = Math.max(0, i-nGrids);
		ir = Math.min(m-1, i+nGrids);
		jl = Math.max(0, j-nGrids);
		jr = Math.min(n-1, j+nGrids);

		int nn = 0;
		int ki, kj, kk, kv;
		for ( ki = il; ki <= ir; ki ++ )
			for ( kj = jl; kj <= jr; kj ++ )
				nn = nn + g[ki][kj].size();
		nodeList = new long[nn];
		kk = 0;
		for ( ki = il; ki <= ir; ki ++ ) {
			for ( kj = jl; kj <= jr; kj ++ ) {
				for ( kv = 0; kv < g[ki][kj].size(); kv ++ ) {
					nodeList[kk] = ((Long)(g[ki][kj].elementAt(kv))).longValue();
					kk = kk + 1;
				}    
			}
		}    
		// debug("query X = " + X + " Y = " + Y + " il = " + il + " i = " + i + " ir = " + ir + " jl = " + jl + " j = " + j + " jr = " + jr + " nodeList size = " + nn);
		channelPort.doSending(new NeighborQueryContract.Message(nodeList));
	}    
}















